#ifndef CONVERTGRAPHATTRIBUTES_H_
#define CONVERTGRAPHATTRIBUTES_H_

#include <assert.h>

#include "graph_support/Graph.h"
#include "graph_support/attributes/LengthEdgeAttributes.h"
#include "graph_support/utils/PtrAccessOpSimulator.h"
#include "graph_support/exceptions/BadGraphItemException.h"


namespace GRAPH_SUPPORT_NS
{
namespace decorate
{

template<
		typename GraphT,
		typename EdgeAttributesT,
		typename EdgeAttributesConverterFn = EdgeAttributesT ( * ) ( typename GraphT::GrEdgePtr & )
		>
class ConvertGraphAttributesTmpl : public Graph
{
private:
	typedef GraphT SubGraph;
	typedef EdgeAttributesConverterFn EdgeAttributesConverter;

public:
	typedef typename SubGraph::GrVertex GrVertex;
	typedef EdgeAttributesT EdgeAttributes;
	class GrEdge
	{
	public:
		GrEdge(
				const typename SubGraph::GrEdgePtr & subEdgePtr,
				EdgeAttributesConverter & edgeAttributesConverter )
			: subEdgePtr( subEdgePtr )
			, edgeAttributes( edgeAttributesConverter( this->subEdgePtr ) )
		{
		}
		GrEdge()
			: subEdgePtr( NULL )
		{
		}
		const GrVertex beg() const
		{
			return subEdgePtr->beg();
		}
		const GrVertex end() const
		{
			return subEdgePtr->end();
		}
		size_t begId() const
		{
			return subEdgePtr->begId();
		}
		size_t endId() const
		{
			return subEdgePtr->endId();
		}
		EdgeAttributes & getAttributes()
		{
			return edgeAttributes;
		}
		bool operator == ( const GrEdge & that ) const
		{
			return this->edgeAttributes == that.edgeAttributes;
		}
	private:
		typename SubGraph::GrEdgePtr subEdgePtr;
		EdgeAttributes edgeAttributes;
	};
	typedef utils::PtrAccessOpSimulator< GrEdge > GrEdgePtr;
	typedef typename SubGraph::VertexIterator VertexIterator;
	class EdgeIterator : public Graph::EdgeIterator
	{
	public:
		typedef int difference_type;
		typedef GrEdgePtr value_type;
		typedef GrEdgePtr * pointer;
		typedef GrEdgePtr & reference;
		typedef std::forward_iterator_tag iterator_category;
	public:
		EdgeIterator(
				const typename SubGraph::EdgeIterator & subIter,
				EdgeAttributesConverter & edgeAttributesConverter )
			: subIter( subIter )
		// TODO: EdgeAttributesConverter &
		// TODO: auto_ptr
			, edgeAttributesConverter( edgeAttributesConverter )
		{
		}
		const EdgeIterator & operator ++ ()
		{
			++subIter;
			return * this;
		}
		bool operator != ( const EdgeIterator & that ) const
		{
			return this->subIter != that.subIter;
		}
		GrEdgePtr operator * ()
		{
			return GrEdgePtr( GrEdge( * subIter, edgeAttributesConverter ) );
		}
		GrEdgePtr * operator -> ()
		{
			return NULL;
		}
	private:
		typename SubGraph::EdgeIterator subIter;
		EdgeAttributesConverter & edgeAttributesConverter;
	};

public:
	ConvertGraphAttributesTmpl(
			SubGraph & gr,
			EdgeAttributesConverter edgeAttributesConverter )
		: gr( gr )
		, edgeAttributesConverter( edgeAttributesConverter )
	{
	}

public:
	size_t getVertexCount() const
	{
		return gr.getVertexCount();
	}
	VertexIterator getVerticesBeg()
	{
		return gr.getVerticesBeg();
	}
	VertexIterator getVerticesEnd()
	{
		return gr.getVerticesEnd();
	}

	EdgeIterator getEdgsBeg( const GrVertex & v )
	{
		return EdgeIterator( gr.getEdgsBeg( v ), edgeAttributesConverter );
	}
	EdgeIterator getEdgsEnd( const GrVertex & v )
	{
		return EdgeIterator( gr.getEdgsEnd( v ), edgeAttributesConverter );
	}

	template< typename FnT >
	void forEachOutEdge( GrVertex v, const FnT & fn );
	EdgeIterator getEdgsBegId( size_t vid )
	{
		return EdgeIterator( gr.getEdgsBegId( vid ), edgeAttributesConverter );
	}
	EdgeIterator getEdgsEndId( size_t vid )
	{
		return EdgeIterator( gr.getEdgsEndId( vid ), edgeAttributesConverter );
	}
	template< typename FnT >
	void forEachOutEdgeId( size_t vid, const FnT & fn );

public:
	size_t vert2num( const GrVertex & v ) const
	{
		return gr.vert2num( v );
	}
	GrVertex num2vert( size_t vid ) const
	{
		return gr.num2vert( vid );
	}

public:
	void addEdge(
			const GrVertex & beg, const GrVertex & end,
			const EdgeAttributes & edgeAttrs = EdgeAttributes() )
	{
		assert( false );
		throw exceptions::BadGraphItemException();
	}

private:
	SubGraph & gr;
	EdgeAttributesConverter edgeAttributesConverter;
};

namespace convert_arguments_utils
{

template < typename GrEdgePtrT >
attributes::Length1EdgeAttributes lengthIs1( GrEdgePtrT & )
{
	return attributes::Length1EdgeAttributes();
}

}

}
}

#endif /* CONVERTGRAPHATTRIBUTES_H_ */
